import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import de.mlp_distributed.mlp.math.mahout.CardinalityException;
import de.mlp_distributed.mlp.math.mahout.DenseMatrix;
import de.mlp_distributed.mlp.math.mahout.DenseVector;
import de.mlp_distributed.mlp.math.mahout.Vector;

public class MT_DenseMatrix extends DenseMatrix {

	static ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);

	public MT_DenseMatrix(final double[][] values) {
		super(values);
	}

	@Override
	public Vector times(final Vector v) {
		final int columns = this.columnSize();
		if (columns != v.size()) {
			throw new CardinalityException(columns, v.size());
		}

		final int cores = Runtime.getRuntime().availableProcessors();
		final int rows = this.rowSize();

		final Vector w = new DenseVector(rows);
		final List<Callable<Integer>> rList = new ArrayList<Callable<Integer>>();
		for (int core = 0; core < cores; core++) {
			rList.add(this.runTimesVector(v, rows, w, core, cores));
		}
		final CompletionService<Integer> ecs = new ExecutorCompletionService<Integer>(MT_DenseMatrix.service);
		for (final Callable<Integer> s : rList) {
			ecs.submit(s);
		}
		final int n = rList.size();
		for (int i = 0; i < n; ++i) {
			try {
				final Integer r = ecs.take().get();
			} catch (final InterruptedException e) {
				e.printStackTrace();
			} catch (final ExecutionException e) {
				e.printStackTrace();
			}
		}
		return w;
	}

	private Callable<Integer> runTimesVector(final Vector v, final int rows, final Vector w, final int core, final int cores) {
		final Callable<Integer> r = new Callable<Integer>() {

			@Override
			public Integer call() throws Exception {
				for (int row = (rows / cores) * core; row < ((rows / cores) * (core + 1)); row++) {
					w.setQuick(row, v.dot(MT_DenseMatrix.this.viewRow(row)));
				}
				return null;
			}
		};
		return r;
	}

}
